//
// Created by konglinglong on 2020/9/6.
//

#include "../include/l3Api.h"
#include "nbm/include/nbmApi.h"
#include "cellm/include/cellmApi.h"
#include "uem/include/uemApi.h"
#include "../../lcore/misc/misc.h"

S_RanL3Context *gpRanL3Context = NULL;

int32_t RAN_L3_LoadCellConfig(S_RanL3CellInst *pCellInst, S_RanCellContext *pCellContext)
{
    pCellInst->inUse = pCellContext->inUse;

    return O5G_RC_OK;
}

int32_t RAN_L3_LoadNodeBConfig(S_RanL3NbInst *pNbInst, S_RanNodeBContext *pNbContext)
{
    int32_t rc;
    uint32_t loop;

    pNbInst->nbId = pNbContext->nbId;
    pNbInst->inUse = pNbContext->inUse;

    /* PLMN */
    pNbInst->plmn.mcc = pNbContext->plmn.mcc;
    pNbInst->plmn.mnc = pNbContext->plmn.mnc;
    pNbInst->plmn.mncLen = pNbContext->plmn.mncLen;

    /* AMF IP */
    memcpy(&pNbInst->amfIp, &pNbContext->amfIp, sizeof(S_O5gIpStr));

    /* UPF IP */
    memcpy(&pNbInst->upfIp, &pNbContext->upfIp, sizeof(S_O5gIpStr));

    pNbInst->cellNum = pNbContext->cellNum;
    for (loop = 0; loop < pNbInst->cellNum; loop++)
    {
        rc = RAN_L3_LoadCellConfig(&pNbInst->cellInst[loop], &pNbContext->cellContext[loop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_LoadNodeBConfig() RAN_L3_LoadCellConfig() error!\n");
    }

    return O5G_RC_OK;
}

int32_t RAN_L3_LoadConfig()
{
    int32_t rc;
    uint32_t loop;

    gpRanL3Context->nbNum = gpRanContext->nbNum;
    for (loop = 0; loop < gpRanL3Context->nbNum; loop++)
    {
        rc = RAN_L3_LoadNodeBConfig(&gpRanL3Context->nbInst[loop], &gpRanContext->nbContext[loop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_LoadConfig() RAN_L3_LoadNodeBConfig() error!\n");
    }

    return O5G_RC_OK;
}

int32_t RAN_L3_Init(void)
{
    int32_t rc;
    uint16_t l3L2UdpPort;
    uint16_t l2L3UdpPort;

    gpRanL3Context = OS_MemCalloc(1, sizeof(S_RanL3Context));
    IF_RETURN(==, NULL, gpRanL3Context, O5G_RC_ER, "RAN_L3_Init() OS_MemCalloc() error!\n");

    rc = RAN_L3_LoadConfig();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() RAN_L3_LoadConfig() error!\n");

    RAN_INIT_INST_IDX(gpRanL3Context);

    rc = OS_SelectInit(&gpRanL3Context->selector, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() OS_SelectInit() error!\n");

    rc = OS_UdpInit(&gpRanL3Context->l2SockFd);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() OS_UdpInit(l2SockFd) error!\n");

    gpRanL3Context->poller = POLLER_Create(POLL_FD_NUM_MAX);
    IF_RETURN(==, NULL, gpRanL3Context->poller, O5G_RC_ER, "RAN_L3_Init() POLLER_Create() error!\n");

    if (APP_TYPE_RAN == gpRanContext->appType)
    {
        l3L2UdpPort = RAN_L3_2_L2_UDP_PORT;
        l2L3UdpPort = RAN_L2_2_L3_UDP_PORT;
    }

    rc = OS_UdpBind(gpRanL3Context->l2SockFd, "127.0.0.1", l2L3UdpPort);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() OS_UdpBind(l2SockFd) error!\n");

    rc = OS_UdpConnect(gpRanL3Context->l2SockFd, "127.0.0.1", l3L2UdpPort);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() OS_UdpConnect(l2SockFd) error!\n");

    rc = RAN_L3_NBM_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() RAN_L3_NBM_Init() error!\n");

    rc = RAN_L3_CELLM_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() RAN_L3_CELLM_Init() error!\n");

    rc = RAN_L3_UEM_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Init() RAN_L3_UEM_Init() error!\n");

    CSP_LOG_INF(RAN_MD_L3_COMM, "RAN_L3_Init() success.\n");

    return O5G_RC_OK;
}

int32_t RAN_L3_Final(void)
{
    int32_t rc;

    rc = OS_SelectFinal(&gpRanL3Context->selector);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Final() OS_SelectFinal() error!\n");

    rc = OS_UdpFinal(gpRanL3Context->l2SockFd);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Final() OS_UdpFinal(l2SockFd) error!\n");

    POLLER_Destroy(gpRanL3Context->poller);

    rc = RAN_L3_NBM_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Final() RAN_L3_NBM_Final() error!\n");

    rc = RAN_L3_CELLM_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Final() RAN_L3_CELLM_Final() error!\n");

    rc = RAN_L3_UEM_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Final() RAN_L3_UEM_Final() error!\n");

    OS_MemFree(gpRanL3Context);

    CSP_LOG_INF(RAN_MD_L3_COMM, "RAN_L3_Final() success.\n");

    return O5G_RC_OK;
}

void RAN_L3_PrdProcess()
{
    RAN_L3_NBM_PrdProcess();
    RAN_L3_CELLM_PrdProcess();
    RAN_L3_UEM_PrdProcess();
}

void RAN_L3_IndProcess(int32_t event, int fd, void *data)
{
    int32_t rc;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L3_IndProcess() OS_SelectRecv() error!\n");

    switch (indMsg.ind)
    {
    case RAN_L3_INTRA_IND_MSG_UE_FSM_EVT:
        RAN_L3_UEM_IndProcess(&indMsg);
        break;
    case RAN_L3_INTRA_IND_MSG_CELL_FSM_EVT:
        RAN_L3_CELLM_IndProcess(&indMsg);
        break;
    case RAN_L3_INTRA_IND_MSG_NB_FSM_EVT:
        RAN_L3_NBM_IndProcess(&indMsg);
        break;
    default:
        CSP_LOG_WRN(RAN_MD_L3_COMM, "RAN_L3_IndProcess() recv unsupport ind[%u]\n", indMsg.ind);
        break;
    }
}

void RAN_L3_MsgProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanCommMsgHdr *pCommMsgHdr;

    rc = recv(fd, gpRanL3Context->sockRecvBuffer, SOCK_BUFFER_SIZE, MSG_DONTWAIT);
    IF_RETURN(>, (int32_t)sizeof(S_RanCommMsgHdr), rc, NO_RETURN_VALUE, "RAN_L3_MsgProcess() recv() error! rc[%d] < %u\n",
        rc, (uint32_t)sizeof(S_RanCommMsgHdr));

    pCommMsgHdr = (S_RanCommMsgHdr *)gpRanL3Context->sockRecvBuffer;
    switch (pCommMsgHdr->type)
    {
    case RAN_L1_API_MSG_PARAMS_RSP:
    case RAN_L1_API_MSG_CONFIG_RSP:
    case RAN_L2_L3_MSG_CONFIG_RSP:
    case RAN_L1_API_MSG_START_RSP:
    case RAN_L2_L3_MSG_START_RSP:
    case RAN_L3_L2_MSG_MIB_TX_RSP:
        RAN_L3_CELLM_MsgProcess(pCommMsgHdr);
        break;
    default:
        CSP_LOG_WRN(RAN_MD_L3_COMM, "RAN_L3_MsgProcess() recv unsupport msg[%#x]\n", pCommMsgHdr->type);
        break;
    }
}

void *RAN_L3_Task(void* arg)
{
    int fd;
    uint64_t lastTimeStamp;

    fd = gpRanL3Context->selector.recvFd;
    POLLER_Add(gpRanL3Context->poller, fd, RAN_L3_IndProcess, NULL, NULL, NULL);

    fd = gpRanL3Context->l2SockFd;
    POLLER_Add(gpRanL3Context->poller, fd, RAN_L3_MsgProcess, NULL, NULL, NULL);

    while (1)
    {
        POLLER_Poll(gpRanL3Context->poller, 1);

        if (CSP_ArrivalDelayTimeUs(&lastTimeStamp, 10000))
        {
            gpRanL3Context->tick10ms++;
            RAN_L3_PrdProcess();
        }

        pthread_testcancel();
    }
    return NULL;
}

int32_t RAN_L3_Start(void)
{
    int32_t rc;

    rc = OS_ThreadCreate(&gpRanL3Context->tid, RAN_L3_Task, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Start() OS_ThreadCreate() error!\n");

    CSP_LOG_INF(RAN_MD_L3_COMM, "RAN_L3_Start() success.\n");

    return O5G_RC_OK;
}

int32_t RAN_L3_Stop(void)
{
    int32_t rc;

    rc = OS_ThreadCancel(gpRanL3Context->tid);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L3_Stop() OS_ThreadCancel() error!\n");

    CSP_LOG_INF(RAN_MD_L3_COMM, "RAN_L3_Stop() success.\n");

    return O5G_RC_OK;
}
